﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;

namespace Xceed.Silverlight.DataGrid
{
  internal class ColumnContainer : DependencyObject, IAnimatedLayoutElement, IRecyclable
  {
    public static readonly DependencyProperty AnimationXProperty = DependencyProperty.Register( "AnimationX", typeof( double ), typeof( ColumnContainer ), new PropertyMetadata( 0d, ColumnContainer.OnAnimationXChanged ) );
    public static readonly DependencyProperty AnimationYProperty = DependencyProperty.Register( "AnimationY", typeof( double ), typeof( ColumnContainer ), new PropertyMetadata( 0d, ColumnContainer.OnAnimationYChanged ) );
    public static readonly DependencyProperty WidthProperty = DependencyProperty.Register( "Width", typeof( double ), typeof( ColumnContainer ), new PropertyMetadata( double.NaN, ColumnContainer.OnWidthChanged ) );
    public static readonly DependencyProperty OpacityProperty = DependencyProperty.Register( "Opacity", typeof( double ), typeof( ColumnContainer ), new PropertyMetadata( 1.0d, ColumnContainer.OnOpacityChanged ) );
    public static readonly DependencyProperty ProjectionProperty = DependencyProperty.Register( "Projection", typeof( Projection ), typeof( ColumnContainer ), new PropertyMetadata( null, ColumnContainer.OnProjectionChanged ) );

    public Column Column { get; set; }

    public object ReferenceItem
    {
      get
      {
        return this.Column;
      }
    }

    public bool IsRecycled
    {
      get
      {
        return m_isRecycled;
      }
      set
      {
        if( m_isRecycled != value )
        {
          m_isRecycled = value;
          this.ApplyToAllChildren( this.UpdateIsRecycled );
        }
      }
    }

    public object RecycleKey
    {
      get
      {
        return this.Column;
      }
    }

    public ColumnPosition ColumnPosition
    {
      get 
      {
        return m_position;
      }

      set 
      {
        if( m_position != value )
        {
          m_position = value;
          foreach( ColumnElementWrapper wrapper in this.Wrappers )
          {
            wrapper.ColumnElement.ParentHost.SetElementPosition( wrapper, value );
          }
        }
      }
    }

    public IEnumerable<ColumnElementWrapper> Wrappers 
    {
      get
      {
        return m_elements;
      }
    }

    public IEnumerable<IColumnElement> Elements
    {
      get
      {
        return m_elements.Select( ( w ) => w.ColumnElement );
      }
    }


    public IEnumerable<ColumnManagerCell> ColumnManagerCells
    {
      get
      {
        return this.GetElementType<ColumnManagerCell>();
      }
    }


    public IEnumerable<Cell> Cells
    {
      get
      {
        return this.GetElementType<Cell>();
      }
    }

    public double Opacity
    {
      get
      {
        return ( double )GetValue( OpacityProperty );
      }
      set
      {
        SetValue( OpacityProperty, value );
      }
    }

    public Projection Projection
    {
      get
      {
        return ( Projection )GetValue( ProjectionProperty );
      }
      set
      {
        SetValue( ProjectionProperty, value );
      }
    }

    public double Width
    {
      get
      {
        return ( double )GetValue( WidthProperty );
      }
      set
      {
        SetValue( WidthProperty, value );
      }
    }

    public Rect? LastArrangeRect
    {
      get
      {
        return m_lastArrangeRect;
      }
      set
      {
        m_lastArrangeRect = value;
      }
    }

    private double ColumnOffset
    {
      get
      {
        return m_columnOffset;
      }
      set
      {
        if( m_columnOffset != value )
        {
          m_columnOffset = value;
          this.ApplyToAllChildren( this.UpdateColumnOffset );
        }

      }
    }

    public double ColumnWidth
    {
      get
      {
        return m_columnWidth;
      }
      set
      {
        m_columnWidth = value;
        this.ApplyToAllChildren( this.UpdateColumnWidth );
      }
    }

    public double AnimationX
    {
      get
      {
        return ( double )GetValue( AnimationXProperty );
      }
      set
      {
        SetValue( AnimationXProperty, value );
      }
    }

    public double AnimationY
    {
      get
      {
        return ( double )GetValue( AnimationYProperty );
      }
      set
      {
        SetValue( AnimationYProperty, value );
      }
    }

    public double Height
    {
      get
      {
        return double.NaN;
      }
      set
      {
        if( !double.IsNaN( value ) )
          throw new InvalidOperationException();
      }
    }

    #region IsDragged Property (Internal)

    internal bool IsDragged
    {
      get
      {
        return m_isDragged;
      }
      set
      {
        if( value != m_isDragged )
        {
          m_isDragged = value;

          foreach( Cell cell in this.Cells )
          {
            cell.SetCommonState( this.IsDragged ? Cell.CommonStateDragged : Cell.CommonStateNormal, true );
          };
        }
      }
    }

    private bool m_isDragged; // false

    #endregion

    #region IsDragPaused Property

    internal bool IsDragPaused
    {
      get
      {
        return m_isDragPaused;
      }
      set
      {
        if( value != m_isDragPaused )
        {
          m_isDragPaused = value;

          foreach( Cell cell in this.Cells )
          {
            cell.OnColumnIsDragPausedChanged();
          };
        }
      }
    }
    private bool m_isDragPaused;

    #endregion

    public Size DesiredSize
    {
      get
      {
        return new Size( this.ColumnWidth, 0 );
      }
    }

    public void Add( ColumnElementWrapper element )
    {
      m_elements.Add( element );

      this.UpdateWidth( element );
      this.UpdateOpacity( element );
      this.UpdateColumnWidth( element );
      this.UpdateColumnOffset( element );
      this.UpdateAnimationX( element );
      this.UpdateAnimationY( element );
      this.UpdateIsRecycled( element );
    }

    public void Remove( ColumnElementWrapper element )
    {
      m_elements.Remove( element );
    }

    public void Clear()
    {
      m_elements.Clear();
    }

    public bool Contains( ColumnElementWrapper element )
    {
      return m_elements.Contains( element );
    }

    private void UpdateColumnOffset( ColumnElementWrapper elem )
    {
      elem.ColumnOffset = this.ColumnOffset;
    }

    private void UpdateColumnWidth( ColumnElementWrapper elem )
    {
      elem.ColumnWidth = this.ColumnWidth;
    }

    private void UpdateWidth( ColumnElementWrapper elem )
    {
      elem.Width = this.Width;
    }

    private void UpdateOpacity( ColumnElementWrapper elem )
    {
      elem.Opacity = this.Opacity;
    }

    private void UpdateProjection( ColumnElementWrapper elem )
    {
      elem.Projection = this.Projection;
    }

    private void UpdateAnimationX( ColumnElementWrapper elem )
    {
      elem.AnimationX = this.AnimationX;
    }

    private void UpdateAnimationY( ColumnElementWrapper elem )
    {
      elem.AnimationY = this.AnimationY;
    }

    private void UpdateIsRecycled( ColumnElementWrapper elem )
    {
      elem.IsRecycled = this.IsRecycled;
    }

    private static void OnWidthChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args )
    {
      ( ( ColumnContainer )obj ).ApplyToAllChildren( ( ( ColumnContainer )obj ).UpdateWidth );
    }

    private static void OnAnimationXChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args )
    {
      ( ( ColumnContainer )obj ).ApplyToAllChildren( ( ( ColumnContainer )obj ).UpdateAnimationX );
    }

    private static void OnAnimationYChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args )
    {
      ( ( ColumnContainer )obj ).ApplyToAllChildren( ( ( ColumnContainer )obj ).UpdateAnimationY );
    }

    private static void OnOpacityChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args )
    {
      ( ( ColumnContainer )obj ).ApplyToAllChildren( ( ( ColumnContainer )obj ).UpdateOpacity );
    }

    private static void OnProjectionChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args )
    {
      ( ( ColumnContainer )obj ).ApplyToAllChildren( ( ( ColumnContainer )obj ).UpdateProjection );
    }

    private void ApplyToAllChildren( Action<ColumnElementWrapper> action )
    {
      foreach( ColumnElementWrapper elem in m_elements )
      {
        action( elem );
      }
    }

    private IEnumerable<T> GetElementType<T>()
    {
      foreach( ColumnElementWrapper wrapper in m_elements )
      {
        Type elementType = wrapper.ColumnElement.GetType();
        if( typeof( T ).IsAssignableFrom( elementType ) )
        {
          yield return ( T )wrapper.ColumnElement;
        }
      }
    }

    public void PrepareBitmapCache()
    {
    }

    public void ClearBitmapCache()
    {
    }

    public void Measure( Size availableSize )
    {
      //useless here. yet.
    }

    public void Arrange( ArrangeParameters parameters )
    {
      Rect arrangeRect = parameters.ArrangePosition;

      if( parameters.LayoutParameters != null )
      {
        LayoutTransitionHelper.ArrangeAnimation( this, parameters.LayoutParameters.TransitionContext, arrangeRect );
      }

      this.LastArrangeRect = arrangeRect;
      this.ColumnOffset = arrangeRect.Left;
    }


    private double m_columnWidth = 100d;
    private double m_columnOffset = 0d;
    private Rect? m_lastArrangeRect;
    private bool m_isRecycled;
    private HashSet<ColumnElementWrapper> m_elements = new HashSet<ColumnElementWrapper>();
    private ColumnPosition m_position = ColumnPosition.Scrollable;



    #region ILayoutElement Members

    object ILayoutElement.ReferenceItem
    {
      get
      {
        return this.ReferenceItem;
      }
    }

    Size ILayoutElement.DesiredSize
    {
      get { return this.DesiredSize; }
    }

    Rect? ILayoutElement.LastArrangeRect
    {
      get
      {
        return this.LastArrangeRect;
      }
      set
      {
        this.LastArrangeRect = value;
      }
    }

    void ILayoutElement.Measure( Size availableSize )
    {
      this.Measure( availableSize );
    }

    void ILayoutElement.Arrange( ArrangeParameters parameters )
    {
      this.Arrange( parameters );
    }

    #endregion

    #region IAnimatedElement interface

    double IAnimatedLayoutElement.Width
    {
      get
      {
        return this.Width;
      }
      set
      {
        this.Width = value;
      }
    }

    double IAnimatedLayoutElement.Height
    {
      get
      {
        return this.Height;
      }
      set
      {
        this.Height = value;
      }
    }

    double IAnimatedLayoutElement.Opacity
    {
      get
      {
        return this.Opacity;
      }
      set
      {
        this.Opacity = value;
      }
    }

    Projection IAnimatedLayoutElement.Projection
    {
      get
      {
        return this.Projection;
      }
      set
      {
        this.Projection = value;
      }
    }

    double IAnimatedLayoutElement.AnimationX
    {
      get
      {
        return this.AnimationX;
      }
      set
      {
        this.AnimationX = value;
      }
    }

    double IAnimatedLayoutElement.AnimationY
    {
      get
      {
        return this.AnimationY;
      }
      set
      {
        this.AnimationY = value;
      }
    }

    void IAnimatedLayoutElement.PrepareBitmapCache()
    {
      this.PrepareBitmapCache();
    }

    void IAnimatedLayoutElement.ClearBitmapCache()
    {
      this.ClearBitmapCache();
    }

    #endregion

    #region IRecyclable Members

    bool IRecyclable.IsRecycled
    {
      get
      {
        return this.IsRecycled;
      }
      set
      {
        this.IsRecycled = value;
      }
    }

    object IRecyclable.RecycleKey
    {
      get { return this.RecycleKey; }
    }

    #endregion
  }
}
